/* UniApo: DMLStatement
 * filename:  DMLStatement.h
 * date:      July 2010
 * author:    Jeffrey Geyssens
 * about:     This file contains the library header for the DMLStatement
 *            SQLStatement subclass:. It contains the specific
 *            implementations for formatting and testing a DMLStatement.

 * Marked as completed: 3rd june 2010 and no changes since
 */

#ifndef _DMLSTATEMENT_H
#define	_DMLSTATEMENT_H
#include "SQLStatement.h"

class DMLStatement : public SQLStatement {
public:
    DMLStatement();
    DMLStatement(const DMLStatement& orig);
    virtual ~DMLStatement();
    bool addPStatement(const string& statement); //add parameterized statement
    void assignRT(string& RT, string& s);
    void pmList();
    void PExecute();
    void rebuildStatement(int option);
protected:

    bool format(); //Removes unneeded whitespaces from statement
    void revert(); //Drops effected table from db

private:
    bool parametered_switch;
    string table; //table relevant to insert query
    map<string, string> attribval; //map of attribute to parameter values
    map<string, string> pmval; //map of parameter to parameter values
    list<string> fields; //list of fields in insert query
    bool formatAttribVal(string a, string v);



};

DMLStatement::DMLStatement() {
    parametered_switch = false;
}

DMLStatement::DMLStatement(const DMLStatement& orig) {
    parametered_switch = orig.parametered_switch;
    statement = orig.statement;
    table = orig.table;
    attribval = orig.attribval;
    fields = orig.fields;
    pmval = orig.pmval;
}

DMLStatement::~DMLStatement() {
}

bool DMLStatement::format() {
    //TODO: Accept lowercase and mixed case INSERT INTO and VALUES token
    type = DML;
    stringstream nospace;

    //First remove all whitespaces, tabs and newlines from the statement
    string::const_iterator iterator1 = statement.begin();

    while (iterator1 != statement.end()) {
        if ((*iterator1 != ' ') && (*iterator1 != '\n')
                && (*iterator1 != '\t'))
            nospace << *iterator1;
        iterator1++;
    }
    //set converted stringstream as statement
    statement = nospace.str();

    //Search for INSERTINTO at start of statement
    int i = statement.find("INSERTINTO");
    if (i != 0) {
        cerr << "ERROR (DMLStatement entry): ";
        cerr << "No INSERT INTO present in DML Statement" << endl;
        return false;
    }
    //Add whitespace between INSERT and INTO
    statement.insert(statement.begin() + 6, ' ');
    //Add whitespace after INTO
    statement.insert(statement.begin() + 11, ' ');

    //Search for left parenthese
    i = statement.find("(");

    if (i == statement.npos) {
        cerr << "ERROR (DML Statement entry): ";
        cerr << "Expected (" << endl;
        return false;
    }
    //Get table name
    //The table name can be found between location 12 and i
    table = statement.substr(12, i - 12);
    //Add whitespace after tablename
    statement.insert(statement.begin() + i, ' ');

    //Now we fix whitespace around VALUES
    //i-j should contain the attribute part
    //j+8 unill end the values part
    int j = statement.find(")VALUES(");

    if (j == statement.npos) {
        cerr << "ERROR (DML Statement entry): ";
        cerr << "Expected keyword VALUES not found" << endl;
        return false;
    }
    statement.insert(statement.begin() + j + 1, ' ');
    statement.insert(statement.begin() + j + 8, ' ');


    if (statement[statement.size() - 2] != ')') {
        cerr << "ERROR (DML Statement entry): ";
        cerr << "Expected )" << endl;
        return false;
    }
    //We now assign attribvals

    //return result of set attribval
    return this->formatAttribVal(statement.substr(i + 1, j - i),
            statement.substr(j + 9, statement.size() - 2 - j - 8));
}

bool DMLStatement::formatAttribVal(string a, string v) {
    //stringstreams to use later
    stringstream ssa, ssv;
    vector<string> va, vv;
    //iterators for input strings
    string::iterator iterator1 = a.begin() + 1; // +1 for '('
    //string::const_iterator iterator2 = v.begin() + 1;
    v.append("@");

    //quickfix for PSQL formatting )) problem

    //first check if colomn count match

    //Fill the vector of attributes
    while (iterator1 != a.end()) {
        if ((*iterator1 == ',') || (*iterator1 == ')')) {
            va.push_back(ssa.str());
            ssa.str("");

            ++iterator1;
        } else {
            ssa << *iterator1;
            ++iterator1;
        }
    }

    //Fill the vector of values
    iterator1 = v.begin() + 1;
    while (iterator1 != v.end()) {
        if ((*iterator1 == ',') || (*iterator1 == '@')) {
            //Stupid fix for parameterizations
            string ret = ssv.str();
            if (*iterator1 == '@') {
                ret = ret.substr(0, ret.size() - 1);

            }

            vv.push_back(ret);
            ssv.str("");
            iterator1++;
        } else {
            ssv << *iterator1;
            iterator1++;
        }
    }

    //error if colomn count is not equal
    if (va.size() != vv.size()) {
        cerr << "ERROR (DML Statement entry): ";
        cerr << "Attribute count is not equal to value count" << endl;
        return false;
    }


    //Now put it all together
    vector<string>::iterator ait = va.begin();
    vector<string>::iterator vit = vv.begin();

    for (ait; ait != va.end(); ++ait) {
        //Update the attribute values map
        attribval.insert(pair<string, string > (*ait, *vit));
        //Update the parameter values map
        pmval.insert(pair<string, string > (*vit, ""));
        ++vit;
    }

    /*test cout: print
    map<string, string>::iterator p;

    for (p = attribval.begin(); p != attribval.end(); p++) {
        cout << p->first << "::" << p->second << endl;
    }
     */



    return true;
}

void DMLStatement::revert() {
    /* No Revert is needed because we can store the generated insert queries
     * after parsing in the local db.
     * When adding to DBS file, we delete all tables after testing after
     * temporarily creating them for each single DML entry
     */


}

bool DMLStatement::addPStatement(const string & st) {
    /* Add Parameterized Statement.
     * This function returns true after constructing the object representing
     * a parameterized sql statement.
     * This is a INSERT Statement of which the actual values which are to be
     * inserted in to the database are replaced with Data Reference Terminals.
     * Notice, it is quite plausible that a DRT might produce multiple
     * symbols in a data string for example (D:Person)(D:Relationship)(D:Person)
     * In order to seperate the symbols from each other in the SQL statement
     * we simply append a distinct numerical value to the DRTs
     * (D:Person[1](D:Relationship[2])(D:Person[3])
     *
     * The input of the user is a string.
     * The parameterization happens automatically in a O(n) procedure
     */

    // First we set the format of the SQL Statement

    this->setStatement(st);

    parametered_switch = true;
    if (this->format()) {
    } else {
        cerr << "Error Formatting Parameterized DML Statemen" << endl;
        return false;
    }
}

void DMLStatement::pmList() {
    map<string, string>::iterator it;
    for (it = attribval.begin(); it != attribval.end(); it++)
        cout << (*it).first << " => " << (*it).second << endl;

}

void DMLStatement::assignRT(string& RT, string & s) {
    //First we check if
    //iterator for pmval (search for RT)
    map<string, string>::iterator i;
    i = pmval.find(RT);
    if (i != pmval.end())
    pmval[RT] = s;
    //this->rebuildStatement(0);
    //having assigned the parameter we can now lookup it's

}

void DMLStatement::PExecute() {
    stringstream attrib, attrival;
}

void DMLStatement::rebuildStatement(int option) {
    stringstream ssattr, ssattrval, result;
    string attr, attrval;
    map<string, string>::iterator p;
    map<string, string>::iterator pmfound;

    for (p = attribval.begin(); p != attribval.end(); ++p) {
        ssattr << p->first << ",";
        if (option == 0) {
            cout << "REBUILDING " << p->first << "::" << p->second << endl;
            ssattrval << p->second << ",";
        }
        if (option == 1) {
            pmfound = pmval.find(p->second);
            if (pmfound != pmval.end()) {

                ssattrval << pmfound->second << ",";
            } else {
                ssattrval << "NO-VAL,";
            }
        }

    }
    attr = ssattr.str();
    attrval = ssattrval.str();
    attr = attr.substr(0, attr.size() - 1);
    attrval = attrval.substr(0, attrval.size() - 1);
    result << "INSERT INTO " << table << "(" << attr << ") VALUES (";
    result << attrval << ");";
    statement = result.str();

}


#endif	/* _DMLSTATEMENT_H */

